#pragma once

#include "HaarCoeff1D.hpp"

namespace zzz{
template <typename T1, typename T2>
struct HaarCoeff2D;
template <typename T1, typename T2>
struct HaarCoeff2DScale;

template <typename T1,typename T2>
class HaarCoeff2D
{
  typedef T1 Index_Type;
  typedef T2 Value_Type;
  struct _coeffrow{
    Index_Type index;
    HaarCoeff1D<T1,T2> rowCoeff;
  }*Coeff;
  size_t Size;
  HaarCoeff2D()
  {
    Coeff=0;
    Size=0;
  }
  ~HaarCoeff2D()
  {
    delete[] Coeff;
  }
  void Clear()
  {
    delete[] Coeff;
    Coeff=0;
    Size=0;
  }
  HaarCoeff2D(const HaarCoeff2D<T1,T2> &c)
  {
    *this=c;
  }
  const HaarCoeff2D& operator=(const HaarCoeff2D<T1,T2> &c)
  {
    Clear();
    Size=c.Size;
    Coeff=new _coeffrow[c.Size];
    for (int i=0; i<Size; i++)
    {
      Coeff[i].index=c.Coeff[i].index;
      Coeff[i].rowCoeff=c.Coeff[i].rowCoeff;
    }
    return *this;
  }
  void LoadFromData(Value_Type *data, Index_Type size, Value_Type threshold=0) //load a size * size matrix data
  {
    Clear();
    int len=0;
    for (Index_Type i=0; i<size; i++)
    {
      Value_Type *curdata=data+i*size;
      for (Index_Type j=0; j<size; j++)
      {
        if (abs(curdata[j])>0) 
        {
          len++;
          break;
        }
      }
    }
    Size=len;
    Coeff=new _coeffrow[len];
    Index_Type cur=0;
    for (Index_Type i=0; i<size; i++)
    {
      Value_Type *curdata=data+i*size;
      Coeff[cur].rowCoeff.LoadFromData(curdata,size,threshold);
      if (Coeff[cur].rowCoeff.Size!=0)
      {
        Coeff[cur].index=i;
        cur++;
      }
    }
  }
  void SaveToData(Value_Type *data, Index_Type size)
  {
    int cur=0;
    for (int i=0; i<size; i++)
    {
      if (cur<Size && Coeff[cur].index==i)
      {
        Coeff[cur].rowCoeff.SaveToData(data+i*size,size);
        cur++;
      }
      else
        memset(data+i*size,0,sizeof(Value_Type)*size);
    }
  }
  inline Value_Type Dot(const HaarCoeff2D<T1,T2> &c)
  {
    Value_Type ret=0;
    int cur1=0,cur2=0;
    for (cur1=0;cur1<Size;cur1++)
    {
      while(cur2<c.Size && Coeff[cur1].index>c.Coeff[cur2].index) cur2++;
      if (Coeff[cur1].index==c.Coeff[cur2].index) ret+=Coeff[cur1].rowCoeff.Dot(c.Coeff[cur2].rowCoeff);
    }
    return ret;
  }
  inline Value_Type Dot(const HaarCoeff2DScale<T1,T2> &c)
  {
    Value_Type ret=0;
    int cur1=0,cur2=0;
    for (cur1=0;cur1<Size;cur1++)
    {
      while(cur2<c.Size && Coeff[cur1].index>c.Coeff[cur2].index) cur2++;
      if (Coeff[cur1].index==c.Coeff[cur2].index) ret+=Coeff[cur1].rowCoeff.Dot(c.Coeff[cur2].rowCoeff);
    }
    return ret;
  }
};

template <typename T1,typename T2>
class HaarCoeff2DScale
{
  typedef T1 Index_Type;
  typedef T2 Value_Type;
  struct _coeffrow{
    Index_Type index;
    HaarCoeff1DScale<T1,T2> rowCoeff;
  }*Coeff;
  size_t Size;
  HaarCoeff2DScale()
  {
    Coeff=0;
    Size=0;
  }
  ~HaarCoeff2DScale()
  {
    delete[] Coeff;
  }
  void Clear()
  {
    delete[] Coeff;
    Coeff=0;
    Size=0;
  }
  HaarCoeff2DScale(const HaarCoeff2DScale<T1,T2> &c)
  {
    *this=c;
  }
  const HaarCoeff2DScale& operator=(const HaarCoeff2DScale<T1,T2> &c)
  {
    Clear();
    Size=c.Size;
    Coeff=new _coeffrow[c.Size];
    for (int i=0; i<Size; i++)
    {
      Coeff[i].index=c.Coeff[i].index;
      Coeff[i].rowCoeff=c.Coeff[i].rowCoeff;
    }
    return *this;
  }
  void LoadFromData(Value_Type *data, Index_Type size, Value_Type threshold=0) //load a size * size matrix data
  {
    Clear();
    int len=0;
    for (Index_Type i=0; i<size; i++)
    {
      Value_Type *curdata=data+i*size;
      for (Index_Type j=0; j<size; j++)
      {
        if (abs(curdata[j])>0) 
        {
          len++;
          break;
        }
      }
    }
    Size=len;
    Coeff=new _coeffrow[len];
    Index_Type cur=0;
    for (Index_Type i=0; i<size; i++)
    {
      Value_Type *curdata=data+i*size;
      Coeff[cur].rowCoeff.LoadFromData(curdata,size,threshold);
      if (Coeff[cur].rowCoeff.Size!=0)
      {
        Coeff[cur].index=i;
        cur++;
      }
    }
  }
  void SaveToData(Value_Type *data, Index_Type size)
  {
    int cur=0;
    for (int i=0; i<size; i++)
    {
      if (cur<Size && Coeff[cur].index==i)
      {
        Coeff[cur].rowCoeff.SaveToData(data+i*size,size);
        cur++;
      }
      else
        memset(data+i*size,0,sizeof(Value_Type)*size);
    }
  }
  inline Value_Type Dot(const HaarCoeff2D<T1,T2> &c)
  {
    Value_Type ret=0;
    int cur1=0,cur2=0;
    for (cur1=0;cur1<Size;cur1++)
    {
      while(cur2<c.Size && Coeff[cur1].index>c.Coeff[cur2].index) cur2++;
      if (Coeff[cur1].index==c.Coeff[cur2].index) ret+=Coeff[cur1].rowCoeff.Dot(c.Coeff[cur2].rowCoeff);
    }
    return ret;
  }
  inline Value_Type Dot(const HaarCoeff2DScale<T1,T2> &c)
  {
    Value_Type ret=0;
    int cur1=0,cur2=0;
    for (cur1=0;cur1<Size;cur1++)
    {
      while(cur2<c.Size && Coeff[cur1].index>c.Coeff[cur2].index) cur2++;
      if (Coeff[cur1].index==c.Coeff[cur2].index) ret+=Coeff[cur1].rowCoeff.Dot(c.Coeff[cur2].rowCoeff);
    }
    return ret;
  }
};

}